package testing.module;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
//import java.awt.event.MouseWheelEvent;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Random;

import testing.control.TestKeyControlScheme;
import utilities.control.keyboard.iface.KeyControlScheme;
import utilities.display.DrawShape;
import utilities.display.definedshapes.DrawCircle;
import utilities.display.definedshapes.DrawPolygon;
import utilities.display.definedshapes.DrawRectangle;
import utilities.display.iface.Drawable;
import utilities.module.GameModule;

/** <code>BoxAndCircle</code><br>
 * <br>
 * An example test class of <code>GameModule</code>. It should produce a pink box that has movement
 * controlled by keyboard, along with a dotted circle that is plain and tracks mouse movement.<br>
 * <br>
 * 
 * @author CoconutTank
 *
 */
public class BoxAndCircle extends GameModule
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 4136831902605004771L;
	
	/**
	 * 
	 */
	protected KeyControlScheme cs;
	
	/**
	 * 
	 */
	protected Container parent;

	/**
	 * 
	 */
	protected DrawRectangle box;

	protected int boxDX = 0;
	protected int boxDXAcc = 2;
	protected int boxDXDeacc = 1;
	protected int boxDXMax = 30;
	
	protected int boxDY = 0;
	protected int boxDYAcc = 2;
	protected int boxDYDeacc = 1;
	protected int boxDYMax = 30;
	
	protected boolean boxUp = false;
	protected boolean boxDown = false;
	protected boolean boxLeft = false;
	protected boolean boxRight = false;

	protected int minBoxX;
	protected int minBoxY;
	protected int maxBoxX;
	protected int maxBoxY;
	
	protected boolean collisionDetected = false;
	
	protected static final int BOX_SIZE = 50;
	
	protected static final int MIN_CORRIDOR_SPACE = 52;
	protected static final int MAX_CORRIDOR_SPACE = 150;
	
	protected double energy = 200.0d;
	
	protected static final NumberFormat LIFE_FORMAT = new DecimalFormat("0000.0");
	protected static final double SCRAPE_DAMAGE = 0.5d;
	protected static final double ENERGY_LOSS = 0.1d;
	
	protected double score = 0.0d;
	
	/**
	 * 
	 */
	protected DrawCircle circle;

	protected static final int CIRCLE_RADIUS = 15;
	
	/**
	 * 
	 */
	protected static final BasicStroke dashed = new BasicStroke(2,
		    BasicStroke.CAP_BUTT,
		    BasicStroke.JOIN_MITER,
		    2,
		    new float[] {4},
		    0);
	
	protected static final BasicStroke longdashed = new BasicStroke(2,
		    BasicStroke.CAP_BUTT,
		    BasicStroke.JOIN_MITER,
		    2,
		    new float[] {12},
		    0);
	
	/**
	 * 
	 */
	//protected ArrayList<DrawRectangle> canyonWallLeft;
	//protected ArrayList<DrawRectangle> canyonWallRight;
	protected ArrayList<DrawShape> canyonWallLeft;
	protected ArrayList<DrawShape> canyonWallRight;

	protected static final int MIN_WALL_DEPTH = 1;
	
	protected int maxWallDepthLeft = 220;	
	protected int maxWallDepthRight = 220;

	protected static final int MIN_WALL_LENGTH = 100;
	protected static final int MAX_WALL_LENGTH = 200;
	
	protected int lastWallDepthLeft = 0;
	protected int lastWallLengthLeft = 0;
	
	protected int lastWallDepthRight = 0;
	protected int lastWallLengthRight = 0;
	
	protected int wallDepthMaxChange = 150;
	protected int wallLengthMaxChange = 10;
	
	protected int currScreenSpeed = 2;
	protected int wallHeightBuffer = -1;
	
	protected int rockyness = 10;
	
	public static final Random rand = new Random();
	
	protected static final BasicStroke bs = new BasicStroke();
	
	/**
	 * 
	 * @param p
	 */
	protected ArrayList<DrawShape> randumbStuff;
	
	protected static final int MIN_RANDUMB_SIZE = 3;
	protected static final int MAX_RANDUMB_SIZE = 300;
	
	protected double chanceOfRandumb = 0.35d;

	public BoxAndCircle(Container p)
	{
		parent = p;
		cs = new TestKeyControlScheme();
		box = new DrawRectangle((parent.getWidth() / 2) - (BOX_SIZE / 2), (parent.getHeight() / 2) - (BOX_SIZE / 2), BOX_SIZE, BOX_SIZE, Color.green, new BasicStroke(5), null, Color.blue, null);
		circle = new DrawCircle(box.getShape().x, box.getShape().y, CIRCLE_RADIUS, Color.black, dashed, null, null, null);
		
		canyonWallLeft = new ArrayList<DrawShape>();
		canyonWallRight = new ArrayList<DrawShape>();
		randumbStuff = new ArrayList<DrawShape>();
		
		lastWallDepthLeft = parent.getWidth() / 4;
		lastWallLengthLeft = parent.getHeight() / 2;
		
		/*
		// make left canyon wall block
		
		// The first block should have a starting depth that's about 25% of the width of the screen
		// Then it'll have a random amount of change, between +25 and -25 (which is wallDepthMaxChange)
		// But the final value should always be between MIN_WALL_DEPTH and MAX_WALL_DEPTH
		lastWallDepth = Math.max(MIN_WALL_DEPTH, Math.min(MAX_WALL_DEPTH, lastWallDepth + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange)));
		
		// The first block should have a starting length that's about 50% of the length of the screen
		// Then it'll have a random amount of change, between +20 and -20 (which is wallLengthMaxChange)
		// the final value should always be between MIN_WALL_LENGTH and MAX_WALL_LENGTH
		lastWallLength = Math.max(MIN_WALL_LENGTH, Math.min(MAX_WALL_LENGTH, lastWallLength + rand.nextInt(wallLengthMaxChange) - rand.nextInt(wallLengthMaxChange)));
		
		RectangleGraphic leftBlk = new RectangleGraphic(0, -lastWallLength, lastWallDepth, lastWallLength, Color.black, bs, Color.black, 0, 0);
		canyonWallLeft.add(leftBlk);
		
		// make right canyon wall block

		// The first block should have a starting depth that's about 25% of the width of the screen
		// Then it'll have a random amount of change, between +25 and -25 (which is wallDepthMaxChange)
		// But the final value should always be between MIN_WALL_DEPTH and MAX_WALL_DEPTH
		lastWallDepth = Math.max(MIN_WALL_DEPTH, Math.min(MAX_WALL_DEPTH, parent.getWidth() / 4 + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange)));

		// The first block should have a starting length that's about 50% of the length of the screen
		// Then it'll have a random amount of change, between +20 and -20 (which is wallLengthMaxChange)
		// the final value should always be between MIN_WALL_LENGTH and MAX_WALL_LENGTH
		lastWallLength = Math.max(MIN_WALL_LENGTH, Math.min(MAX_WALL_LENGTH, parent.getHeight() / 2 + rand.nextInt(wallLengthMaxChange) - rand.nextInt(wallLengthMaxChange)));
		
		RectangleGraphic rightBlk = new RectangleGraphic(parent.getWidth() - lastWallDepth, -lastWallLength, lastWallDepth, lastWallLength, Color.black, bs, Color.black, 0, 0);
		canyonWallRight.add(rightBlk);
		*/
		
		// Get box boundaries (for staying on screen)
		minBoxX = 0; //Math.abs(box.getDrawDX());
		minBoxY = 0; //Math.abs(box.getDrawDY());

		//maxBoxX = (int) (parent.getWidth() - box.width) - parent.getInsets().right - parent.getInsets().left;//Math.abs(box.getDrawDX());// - parent.getInsets().right - parent.getInsets().left;
		maxBoxX = (int) (parent.getWidth() - box.getShape().getWidth()) - parent.getInsets().right - parent.getInsets().left;//Math.abs(box.getDrawDX());// - parent.getInsets().right - parent.getInsets().left;
		//maxBoxY = (int) (parent.getHeight() - box.height) - parent.getInsets().bottom - parent.getInsets().top;//Math.abs(box.getDrawDY());// - parent.getInsets().bottom - parent.getInsets().top;
		maxBoxY = (int) (parent.getHeight() - box.getShape().getHeight()) - parent.getInsets().bottom - parent.getInsets().top;//Math.abs(box.getDrawDY());// - parent.getInsets().bottom - parent.getInsets().top;
	}

	public void drawMe(Graphics g) 
	{
		// Get Graphics2D
		Graphics2D g2 = (Graphics2D)g;
		
		// Back up old settings
		Color c = g2.getColor();
		Stroke s = g2.getStroke();
		Composite a = g2.getComposite();
		
		// Back up old settings
		//g2.setComposite(comp)
		//AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.DST_OVER, 0.5f);
		//Composite a = g2.getComposite();
		
		// draw randumbs
		//g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
		//g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, rand.nextFloat()));
		for(Drawable gfx : randumbStuff)
		{
			if(gfx != null)
			{
				gfx.drawMe(g2);
			}			
		}
		
		// Reset old settings
		//g2.setComposite(a);
		
		// draw canyon walls
		// Left side
		for(Drawable gfx : canyonWallLeft)
		{
			if(gfx != null)
			{
				gfx.drawMe(g2);
				
				// Debug code; used to find origin
				//g2.setColor(Color.red);
				//g2.fillRect(gfx.x, gfx.y, 5, 5);
			}
		}
		// Right side
		for(Drawable gfx : canyonWallRight)
		{
			if(gfx != null)
			{
				gfx.drawMe(g2);
				
				// Debug code; used to find origin
				//g2.setColor(Color.red);
				//g2.fillRect(gfx.x, gfx.y, 5, 5);
			}
		}
		
		// draw main objects
		box.drawMe(g2);
		circle.drawMe(g2);
		
		// Draw line from box center to circle center
		g2.setStroke(longdashed);
		g2.setColor(Color.black);
		g2.drawLine(box.getShape().x + box.getShape().width / 2, box.getShape().y + box.getShape().height / 2, circle.getX(), circle.getY());
		
		// Draw "stats"
		g2.setColor(Color.red);
		g2.drawString("Speed: " + currScreenSpeed, 2, 13);
		g2.drawString("Status: " + (energy >= 0.01d ? (collisionDetected ? "TAKING DAMAGE" : "Stable") : "DESTROYED"), 2, 25);
		g2.drawString("Energy: " + (energy >= 0.01d ? String.format("%.1f", energy) : "X_X"), 2, 37);
		g2.drawString("Score: " + String.format("%.0f", score), 2, 49);
		
		// Reset old settings
		g2.setColor(c);
		g2.setStroke(s);
		g2.setComposite(a);
	}

	public void keyPressed(KeyEvent e) 
	{
		if(e.getKeyCode() == KeyEvent.VK_UP
		   || e.getKeyCode() == KeyEvent.VK_W)
		{
			boxUp = true;
		}
		
		if(e.getKeyCode() == KeyEvent.VK_DOWN
		   || e.getKeyCode() == KeyEvent.VK_S)
		{
			boxDown = true;
		}

		if(e.getKeyCode() == KeyEvent.VK_LEFT
		   || e.getKeyCode() == KeyEvent.VK_A)
		{
			boxLeft = true;
		}

		if(e.getKeyCode() == KeyEvent.VK_RIGHT
		   || e.getKeyCode() == KeyEvent.VK_D)
		{
			boxRight = true;
		}
	}

	public void keyReleased(KeyEvent e) 
	{
		if(e.getKeyCode() == KeyEvent.VK_UP
		   || e.getKeyCode() == KeyEvent.VK_W)
		{
			boxUp = false;
		}
		
		if(e.getKeyCode() == KeyEvent.VK_DOWN
		   || e.getKeyCode() == KeyEvent.VK_S)
		{
			boxDown = false;
		}

		if(e.getKeyCode() == KeyEvent.VK_LEFT
		   || e.getKeyCode() == KeyEvent.VK_A)
		{
			boxLeft = false;
		}

		if(e.getKeyCode() == KeyEvent.VK_RIGHT
		   || e.getKeyCode() == KeyEvent.VK_D)
		{
			boxRight = false;
		}
	}

	public void mouseMoved(MouseEvent e) 
	{
		// Get insets!
		//circle.x = e.getX() - parent.getInsets().left;
		//circle.y = e.getY() - parent.getInsets().top;
		circle.setLocation(e.getX() - parent.getInsets().left, e.getY() - parent.getInsets().top);
	}

	@Override
	public void update() 
	{
		// Pretend that the box isn't running into anything...
		collisionDetected = false;
		
		// Move canyon walls
		moveLeftWall();
		moveRightWall();
		//moveRandumbStuff();
		
		// Move the box
		moveBox();
		
		// Change the box's color, depending on whether any collision was detected or not
		box.setFillColor(collisionDetected ? Color.red : Color.blue);
		
		// Haha oh u funny
		box.setLineColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
		
		// Set screen speed, which is relative to how far up/down the ship is on the screen
		//currScreenSpeed = Math.max(1, (int)(40.0 * (parent.getHeight() - box.y) / parent.getHeight()) - 4);
		currScreenSpeed = Math.max(1, (int)(40.0 * (parent.getHeight() - box.getShape().y) / parent.getHeight()) - 4);
		
		// Set chance that a randumb object will appear on the screen
		//chanceOfRandumb = Math.max(0.1f, 0.90 * (parent.getHeight() - box.y) / parent.getHeight());
		chanceOfRandumb = Math.max(0.1f, 0.90 * (parent.getHeight() - box.getShape().y) / parent.getHeight());
		
		// Is the ship taking damage?
		if(energy >= 0.01d && collisionDetected) energy -= SCRAPE_DAMAGE;
		
		// If the ship is alive... increase the score!
		if(energy >= 0.01d) score += currScreenSpeed;
		
		// Ship is slowly losing energy anyway...
		energy -= ENERGY_LOSS;
	}

	private void moveLeftWall()
	{
		// Check if there are no more follow up blocks;
		// If so, create some!
		//if(canyonWallLeft.size() > 0 && canyonWallLeft.get(canyonWallLeft.size() - 1).y >= wallHeightBuffer)
		//if(canyonWallLeft.size() > 0 && canyonWallLeft.get(canyonWallLeft.size() - 1).y >= (-currScreenSpeed + wallHeightBuffer))
		//if(canyonWallLeft.size() <= 0 || (canyonWallLeft.size() > 0 && canyonWallLeft.get(canyonWallLeft.size() - 1).y >= -currScreenSpeed))
		if(canyonWallLeft.size() <= 0 || (canyonWallLeft.size() > 0 && ((DrawPolygon)canyonWallLeft.get(canyonWallLeft.size() - 1)).getY(0) >= -currScreenSpeed))
		{
			// Determine maxWallDepthLeft
			// It should never be greater than parent.getWidth() - maxWallDepthRight - MIN_CORRIDOR_SPACE, but can be almost anything lesser
			maxWallDepthLeft = Math.max(MIN_WALL_DEPTH, Math.min(parent.getWidth() - maxWallDepthRight - MIN_CORRIDOR_SPACE, maxWallDepthLeft + (rand.nextBoolean() ? rand.nextInt(wallDepthMaxChange) : -rand.nextInt(wallDepthMaxChange))));
			
			// The first block should have a starting depth that's about 25% of the width of the screen
			// Then it'll have a random amount of change, between +25 and -25 (which is wallDepthMaxChange)
			// But the final value should always be between MIN_WALL_DEPTH and maxWallDepthLeft
			//lastWallDepthLeft = Math.max(MIN_WALL_DEPTH, Math.min(maxWallDepthLeft, lastWallDepthLeft + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange)));
			
			// The first block should have a starting length that's about 50% of the length of the screen
			// Then it'll have a random amount of change, between +20 and -20 (which is wallLengthMaxChange)
			// the final value should always be between MIN_WALL_LENGTH and MAX_WALL_LENGTH
			lastWallLengthLeft = Math.max(MIN_WALL_LENGTH, Math.min(MAX_WALL_LENGTH, lastWallLengthLeft + rand.nextInt(wallLengthMaxChange) - rand.nextInt(wallLengthMaxChange)));

			//int randWidth = rand.nextInt(MAX_WALL_WIDTH);		
			//int randHeight = rand.nextInt(MAX_WALL_HEIGHT);

			//DrawRectangle wallBlk = new DrawRectangle(0, -lastWallLengthLeft, lastWallDepthLeft, lastWallLengthLeft, Color.green, bs, null, Color.black, null);
			//RectangleGraphic wallBlk = new RectangleGraphic(0, (canyonWallLeft.size() <= 0 ? -lastWallLengthLeft : -canyonWallLeft.get(canyonWallLeft.size() - 1).y), lastWallDepthLeft, lastWallLengthLeft, Color.black, bs, Color.black, 0, 0);

			DrawPolygon wallBlk = new DrawPolygon(Color.black, bs, null, Color.black, null);
			
			// First two points should be top right and bottom left, flush with the wall
			int x1 = 0;
			int y1 = -lastWallLengthLeft;
			
			wallBlk.addPoint(x1, y1);
			
			int x2 = 0;
			int y2 = 0;
			
			wallBlk.addPoint(x2, y2);
			
			// Third point should use the last determined wall depth (from the previous block)			
			int x3 = lastWallDepthLeft;
			int y3 = 0;
			
			wallBlk.addPoint(x3, y3);

			// "Remaining" points will have random depth
			int newPointAdd = rand.nextInt(rockyness) + 1;
			
			for(int i = 1; i <= newPointAdd; i++)
			{
				int x4 = (lastWallDepthLeft = Math.max(MIN_WALL_DEPTH, Math.min(maxWallDepthLeft, lastWallDepthLeft + (rand.nextBoolean() ? rand.nextInt(wallDepthMaxChange) : -rand.nextInt(wallDepthMaxChange)))));
				int y4 = (int)(-lastWallLengthLeft * 1.0 * i / newPointAdd);
				
				wallBlk.addPoint(x4, y4);
			}
			
			canyonWallLeft.add(wallBlk);
		}
		
		// Keep track of canyon wall pieces to keep
		ArrayList<DrawShape> keepMe = new ArrayList<DrawShape>();
		
		// Update each canyon wall piece on the left
		//for(DrawRectangle gfx : canyonWallLeft)
		for(DrawShape gfx : canyonWallLeft)
		{
			if(gfx != null)
			{
				//gfx.y += currScreenSpeed;
				//gfx.getShape().y += currScreenSpeed;
				((DrawPolygon)gfx).translate(0, currScreenSpeed);
				
				// Is the canyon wall piece still going to remain on screen?
				//if(gfx.y < parent.getHeight())
				//if(gfx.getShape().y < parent.getHeight())
				if(((DrawPolygon)gfx).getY(0) < parent.getHeight())
				{
					//System.out.println("gfx.y = " + gfx.y);
					//System.out.println("parent.getHeight() = " + parent.getHeight());
					keepMe.add(gfx);
				}
				
				// Found a collision yet?
				//if(!collisionDetected && gfx.intersects(box.x, box.y, box.x + box.width, box.y + box.height)) collisionDetected = true;
				if(!collisionDetected && gfx.getShape().intersects(box.getShape())) collisionDetected = true;
			}
		}
		
		// Set canyon wall left to the remaining pieces
		canyonWallLeft = keepMe;
	}

	private void moveRightWall()
	{
		// Check if there are no more follow up blocks;
		// If so, create some!
		//if(canyonWallRight.size() > 0 && canyonWallRight.get(canyonWallRight.size() - 1).y >= wallHeightBuffer)
		//if(canyonWallRight.size() > 0 && canyonWallRight.get(canyonWallRight.size() - 1).y >= (-currScreenSpeed + wallHeightBuffer))
		//if(canyonWallRight.size() <= 0 || (canyonWallRight.size() > 0 && canyonWallRight.get(canyonWallRight.size() - 1).y >= -currScreenSpeed))
		if(canyonWallRight.size() <= 0 || (canyonWallRight.size() > 0 && ((DrawPolygon)canyonWallRight.get(canyonWallRight.size() - 1)).getY(0) >= -currScreenSpeed))
		{
			// Determine maxWallDepthRight
			// It should never be greater than parent.getWidth() - maxWallDepthLeft - MIN_CORRIDOR_SPACE, but can be almost anything lesser
			maxWallDepthRight = Math.max(MIN_WALL_DEPTH, Math.min(parent.getWidth() - maxWallDepthLeft - MIN_CORRIDOR_SPACE, maxWallDepthRight + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange)));
			
			// The first block should have a starting depth that's about 25% of the width of the screen
			// Then it'll have a random amount of change, between +25 and -25 (which is wallDepthMaxChange)
			// But the final value should always be between MIN_WALL_DEPTH and maxWallDepthRight
			//lastWallDepthRight = Math.max(MIN_WALL_DEPTH, Math.min(maxWallDepthRight, lastWallDepthRight + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange)));
			
			// The first block should have a starting length that's about 50% of the length of the screen
			// Then it'll have a random amount of change, between +20 and -20 (which is wallLengthMaxChange)
			// the final value should always be between MIN_WALL_LENGTH and MAX_WALL_LENGTH
			lastWallLengthRight = Math.max(MIN_WALL_LENGTH, Math.min(MAX_WALL_LENGTH, lastWallLengthRight + rand.nextInt(wallLengthMaxChange) - rand.nextInt(wallLengthMaxChange)));
			
			//int randWidth = rand.nextInt(MAX_WALL_WIDTH);		
			//int randHeight = rand.nextInt(MAX_WALL_HEIGHT);
			
			//DrawRectangle wallBlk = new DrawRectangle(parent.getWidth() - lastWallDepthRight, -lastWallLengthRight, lastWallDepthRight, lastWallLengthRight, Color.green, bs, null, Color.black, null);

			DrawPolygon wallBlk = new DrawPolygon(Color.black, bs, null, Color.black, null);
			
			// First two points should be top right and bottom right, flush with the wall
			int x1 = parent.getWidth();
			int y1 = -lastWallLengthRight;
			
			wallBlk.addPoint(x1, y1);
			
			int x2 = parent.getWidth();
			int y2 = 0;
			
			wallBlk.addPoint(x2, y2);
			
			// Third point should use the last determined wall depth (from the previous block)			
			int x3 = parent.getWidth() - lastWallDepthRight;
			int y3 = 0;
			
			wallBlk.addPoint(x3, y3);

			// "Remaining" points will have random depth
			int newPointAdd = rand.nextInt(rockyness) + 1;
			
			for(int i = 1; i <= newPointAdd; i++)
			{
				int x4 = parent.getWidth() - (lastWallDepthRight = Math.max(MIN_WALL_DEPTH, Math.min(maxWallDepthRight, lastWallDepthRight + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange))));
				int y4 = (int)(-lastWallLengthRight * 1.0 * i / newPointAdd);
				
				wallBlk.addPoint(x4, y4);
			}
			
			canyonWallRight.add(wallBlk);
			
		}
		
		// Keep track of canyon wall pieces to keep
		ArrayList<DrawShape> keepMe = new ArrayList<DrawShape>();
		
		// Update each canyon wall piece on the right
		for(DrawShape gfx : canyonWallRight)
		{
			if(gfx != null)
			{
				//gfx.y += currScreenSpeed;
				//gfx.getShape().y += currScreenSpeed;
				((DrawPolygon)gfx).translate(0, currScreenSpeed);
				
				// Is the canyon wall piece still going to remain on screen?
				//if(gfx.y < parent.getHeight())
				//if(gfx.getShape().y < parent.getHeight())
				if(((DrawPolygon)gfx).getY(0) < parent.getHeight())
				{
					//System.out.println("gfx.y = " + gfx.y);
					//System.out.println("parent.getHeight() = " + parent.getHeight());
					keepMe.add(gfx);
				}
				
				// Found a collision yet?
				//if(!collisionDetected && gfx.intersects(box.x, box.y, box.x + box.width, box.y + box.height)) collisionDetected = true;
				if(!collisionDetected && gfx.getShape().intersects(box.getShape())) collisionDetected = true;
			}
		}
		
		// Set canyon wall right to the remaining pieces
		canyonWallRight = keepMe;
	}

	private void moveRandumbStuff()
	{
		// Check if there are no more follow up blocks;
		// If so, create some!
		//if(randumbStuff.size() <= 0 || (randumbStuff.size() > 0 && randumbStuff.get(randumbStuff.size() - 1).y >= -currScreenSpeed))
		//if(rand.nextDouble() <= 0.35d)
		if(rand.nextDouble() <= chanceOfRandumb)
		{
			// The first block should have a starting depth that's about 25% of the width of the screen
			// Then it'll have a random amount of change, between +25 and -25 (which is wallDepthMaxChange)
			// But the final value should always be between MIN_WALL_DEPTH and MAX_WALL_DEPTH
			//lastWallDepth = Math.max(MIN_WALL_DEPTH, Math.min(MAX_WALL_DEPTH, lastWallDepth + rand.nextInt(wallDepthMaxChange) - rand.nextInt(wallDepthMaxChange)));
			int randumbWidth = Math.max(MIN_RANDUMB_SIZE, rand.nextInt(MAX_RANDUMB_SIZE));
			
			// The first block should have a starting length that's about 50% of the length of the screen
			// Then it'll have a random amount of change, between +20 and -20 (which is wallLengthMaxChange)
			// the final value should always be between MIN_WALL_LENGTH and MAX_WALL_LENGTH
			//lastWallLength = Math.max(MIN_WALL_LENGTH, Math.min(MAX_WALL_LENGTH, lastWallLength + rand.nextInt(wallLengthMaxChange) - rand.nextInt(wallLengthMaxChange)));
			int randumbHeight = Math.max(MIN_RANDUMB_SIZE, rand.nextInt(MAX_RANDUMB_SIZE));
			
			//int randWidth = rand.nextInt(MAX_WALL_WIDTH);		
			//int randHeight = rand.nextInt(MAX_WALL_HEIGHT);

			//AlphaComposite lineAlpha = AlphaComposite.getInstance(AlphaComposite.DST_OVER, rand.nextFloat());
			//AlphaComposite fillAlpha = AlphaComposite.getInstance(AlphaComposite.DST_OVER, rand.nextFloat());
			
			AlphaComposite lineAlpha = AlphaComposite.getInstance(AlphaComposite.DST_OVER, 0.5f);
			AlphaComposite fillAlpha = AlphaComposite.getInstance(AlphaComposite.DST_OVER, 0.5f);

			Color randumbLineColor = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
			Color randumbFillColor = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
			
			if(rand.nextBoolean())
			{
				DrawRectangle wallBlk = new DrawRectangle(rand.nextInt(parent.getWidth()), -randumbHeight, randumbWidth, randumbHeight, randumbLineColor, bs, lineAlpha, randumbFillColor, fillAlpha);
				randumbStuff.add(wallBlk);
			}
			else
			{
				//OvalGraphic wallBlk = new OvalGraphic(rand.nextInt(parent.getWidth()), -randumbHeight, randumbWidth, Color.black, bs, randumbColor, 0, 0);
				DrawCircle wallBlk = new DrawCircle(rand.nextInt(parent.getWidth()), -randumbHeight, randumbWidth, randumbLineColor, bs, lineAlpha, randumbFillColor, fillAlpha);
				randumbStuff.add(wallBlk);
			}
		}
		
		// Keep track of canyon wall pieces to keep
		ArrayList<DrawShape> keepMe = new ArrayList<DrawShape>();
		
		// Update each canyon wall piece on the right
		for(DrawShape gfx : randumbStuff)
		{
			if(gfx != null)
			{
				if(gfx instanceof DrawRectangle)
				{
					DrawRectangle rect = (DrawRectangle)gfx;
					
					rect.getShape().y += currScreenSpeed;
					
					// Is the wall block being displayed no longer on screen?
					if(rect.getShape().y < parent.getHeight())
					{
						//System.out.println("gfx.y = " + gfx.y);
						//System.out.println("parent.getHeight() = " + parent.getHeight());
						keepMe.add(gfx);
					}
				}
				else if(gfx instanceof DrawCircle)
				{
					DrawCircle cir = (DrawCircle)gfx;
					
					cir.translate(0, currScreenSpeed);
					
					if(cir.getY() < parent.getHeight())
					{
						keepMe.add(gfx);
					}
				}
			}
		}
		
		// Set canyon wall right to the remaining pieces
		randumbStuff = keepMe;
	}

	private void moveBox() 
	{
		// Do acceleration here
		if(boxUp) boxDY -= boxDYAcc;
		if(boxDown) boxDY += boxDYAcc;
		if(boxLeft) boxDX -= boxDXAcc;
		if(boxRight) boxDX += boxDXAcc;
		
		// Do deceleration here
		if(!boxUp && boxDY < 0) boxDY += boxDYDeacc;
		if(!boxDown && boxDY > 0) boxDY -= boxDYDeacc;
		if(!boxLeft && boxDX < 0) boxDX += boxDXDeacc;
		if(!boxRight && boxDX > 0) boxDX -= boxDXDeacc;
		
		// Do max velocity checks here
		if(boxDX > boxDXMax) boxDX = boxDXMax;
		if(boxDX < -boxDXMax) boxDX = -boxDXMax;
		if(boxDY > boxDYMax) boxDY = boxDYMax;
		if(boxDY < -boxDYMax) boxDY = -boxDYMax;
		
		// Do actual movements here
		box.getShape().x += boxDX;
		box.getShape().y += boxDY;
		
		// Keep the box within the screen boundaries
		if(box.getShape().x < minBoxX) box.getShape().x = minBoxX;
		if(box.getShape().y < minBoxY) box.getShape().y = minBoxY;
		
		if(box.getShape().x >= maxBoxX) box.getShape().x = maxBoxX;
		if(box.getShape().y >= maxBoxY) box.getShape().y = maxBoxY;
	}
}
